import datetime
import logging
import re
from random import randint

from flask import request, abort, make_response, jsonify, session

from info import constants, redis_store, db
from info.libs.yuntongxun.sms import CCP
from info.models import User
from info.response_code import RET
from info.utils.captcha.captcha import captcha
from . import passport_blue


@passport_blue.route('/image_code', methods=['GET'])
def get_image_code():
    """提供图片验证码"""
    # 1.获取参数image_codeid 
    image_code_id = request.args.get('imageCodeId')
    print(request.args)
    # 2.校验参数，如果没有403
    if not image_code_id:
        abort(400)
    # 3.生成图片验证码的文字和图片信息：text是图片验证码的文字；image是图片验证码图片
    # 注意点：需要在虚拟环境中安装pillow模块
    name, text, image = captcha.generate_captcha()

    # 4.将imageCodeId和图片验证码文字绑定到redis:图片验证码五分钟之后自动的过期
    try:
        redis_store.set('ImageCode:' + image_code_id, text, ex=constants.IMAGE_CODE_REDIS_EXPIRES)
    except Exception as e:
        logging.error(e)
        abort(500)
    # 5.修改image的'Content-Type'为'image/jpg'
    response = make_response(image)
    response.headers['Content-Type'] = 'image/jpg'
    # 6.响应图片验证码
    return response


@passport_blue.route('/sms_code', methods=['GET', 'POST'])
def send_sms_code():
    """发送短信验证码
    1.接受参数：mobile,image_code,image_code_id
    2.校验参数：判断参数是否齐全，判断手机号是否正确
    3.使用image_code_id从redis中读取存储于服务器的图片验证码
    4.使用用户输入的图片验证码对比服务器里存储的图片验证码
    5.如果对比成功生成短信验证码，长度是6位的随机数字
    6.再使用ccp()单例发送短信
    7.发送短信验证码成功，就把短信验证码存到redis()中
    8.返回验证码发送的结果
     """
    # 1.接受参数：mobile,image_code,image_code_id
    json_dict = request.json
    mobile = json_dict.get('mobile')
    image_code_client = json_dict.get('image_code')
    image_code_id = json_dict.get('image_code_id')
    # 2.校验参数：判断参数是否齐全，判断手机号是否正确
    if not all([mobile, image_code_client, image_code_id]):
        return jsonify(errno=RET.PARAMERR, errmsg='缺少必传参数')

    if not re.match('^1[345678][0-9]{9}$', mobile):
        return jsonify(errno=RET.PARAMERR, errmsg='手机号码格式错误')
    # 3.使用image_code_id从redis中读取存储于服务器的图片验证码
    try:
        image_code_server = redis_store.get('ImageCode:' + image_code_id)
    except Exception as e:
        logging.error(e)
        return jsonify(errno=RET.DBERR, errmsg='查询图片验证码失败')
        # return jsonify(errno=RET.DBERR,errmsg='')

    # 判断image_code_server是否存在
    if not image_code_server:
        return jsonify(errno=RET.NODATA, errmsg='图片验证码不存在')
    # 4.使用用户输入的图片验证码对比服务器里存储的图片验证码
    if image_code_client.lower() != image_code_server.lower():
        return jsonify(errno=RET.PARAMERR, errmsg='图片验证码输入有误')

    # 5.如果对比成功生成短信验证码，长度是6位的l随机数字，不够6位补0
    sms_code = '%06d' % randint(0, 999999)
    logging.debug(sms_code)

    # 6.再使用ccp()单例发送短信
    # result = CCP().send_sms_code(mobile,[sms_code,5],1)
    # if result != 0:
    #     return jsonify(errno=RET.THIRDERR,errmsg='发送短信失败')
    #  7.发送短信验证码成功，就把短信验证码存到redis()中
    try:
        redis_store.set('SMS:' + mobile, sms_code, constants.SMS_CODE_REDIS_EXPIRES)
    except Exception as e:
        logging.error(e)
        return jsonify(errno=RET.DATAERR, errmsg='存储短信失败')

    #  8.返回验证码发送的结果
    return jsonify(errno=RET.OK, errmsg='发送短信成功')


@passport_blue.route('/register', methods=['POST'])
def register():
    """注册
    1.接受参数：mobile,smscode,password(明文)
    2.校验参数：参数是否齐全，校验手机号码格式是否正确
    3.使用mobile读取redis短信验证码
    4.使用客户端传入的短信验证码和服务端的验证码进行对比
    5.创建User模型对象，并给用户属性赋值
    6.记得保存最后一次登录的时间（last_login，为了实现注册即登录）
    7.需要将用户注册数据保存到mysql数据库
    8.将状态保持信息写入到session（为了实现注册即登录）
    9.返回注册结果
    """

    # 1.接受参数：mobile,smscode,password(明文)
    json_dict = request.json
    mobile = json_dict.get('mobile')
    sms_code_clien = json_dict.get('smscode')
    password = json_dict.get('password')

    # 2.校验参数：参数是否齐全，校验手机号码格式是否正确
    if not all([mobile, sms_code_clien, password]):
        return jsonify(errno=RET.PARAMERR, errmsg='缺少必传参数')
    if not re.match('^1[345678][0-9]{9}$', mobile):
        return jsonify(errno=RET.PARAMERR, errmsg='手机号格式错误')

    # 3.使用mobile读取redis短信验证码
    try:
        sms_code_server = redis_store.get('SMS:' + mobile)
    except Exception as e:
        logging.error(e)
        return jsonify(errno=RET.DBERR, errmsg='查询短信验证码失败')
    if not sms_code_server:
        return jsonify(errno=RET.NODATA, errmsg='短信验证码不存在')

    # 4.使用客户端传入的短信验证码和服务端的验证码进行对比
    if sms_code_server != sms_code_clien:
        return jsonify(errno=RET.PARAMERR, errmsg='短信验证码输入错误')

    # 5.创建User模型对象，并给用户属性赋值
    user = User()
    user.nick_name = mobile  # 默认手机号就是用户名
    # user.password_hash = password  #password_hash:需要存储密文 password是明文，后续优化
    user.mobile = mobile

    # password_hash:需要存储密文 password是明文
    # password:是我即将给User模型类增加的属性（python是面向对象的高级编程语言，可以在代码运行的过程中给对象添加属性）
    # 在password属性的setter方法中，拿着明文密码进行加密处理，在保存到password_hash
    user.password = password
    # 6.记得保存最后一次登录的时间（last_login，为了实现注册即登录）
    user.last_login = datetime.datetime.now()  # 最后一次登录时间（实现注册即登录）

    # 7.需要将用户注册数据保存到mysql数据库
    try:
        db.session.add(user)
        db.session.commit()
    except Exception as e:
        logging.error(e)
        db.session.rollback()
        return jsonify(errno=RET.DATAERR, errmsg='存储用户数据失败')

    # 9.返回注册结果
    return jsonify(errno=RET.OK, errmsg='注册成功')


@passport_blue.route('/login', methods=['POST'])
def login():
    """登录
    1.接受参数：mobile,password(明文)
    2.校验参数:参数是否齐全，校验手机号码格式是否正确
    3.使用mobile查询user是否存在
    4.如果user存在，再去校验密码
    5.如果密码校验通过，就记录最后一次登录的时间和状态保持
    6.响应登录结果
    """
    # 1.接受参数：mobile,password(明文)
    json_dict = request.json
    mobile = json_dict.get('mobile')
    password = json_dict.get('password')

    # 2.校验参数:参数是否齐全，校验手机号码格式是否正确
    if not all([mobile, password]):
        return jsonify(errno=RET.PARAMERR, errmsg='缺少必要的参数')
    if not re.match('^1[345678][0-9]{9}$', mobile):
        return jsonify(errno=RET.PARAMERR, errmsg='手机号码输入有误')

    # 3.使用mobile查询user是否存在
    try:
        user = User.query.filter(User.mobile == mobile).first()
    except Exception as e:
        logging.error(e)
        return jsonify(errno=RET.DBERR, errmsg='用户名或密码错误')
    if not user:
        return jsonify(errno=RET.NODATA, errmsg='用户名或密码错误')

    # 4.如果user存在，再去校验密码
    if not user.check_password(password):
        return jsonify(errno=RET.PARAMERR, errmsg='用户名或密码错误')

    # 5.如果密码校验通过，就记录最后一次登录的时间和状态保存
    user.last_login = datetime.datetime.now()
    try:
        db.session.commit()  # 修改记录不需要add
    except Exception as e:
        logging.error(e)
        db.session.rollback()
        return jsonify(errno=RET.DATAERR, errmsg='登录失败')

    # 状态保持
    session['user_id'] = user.id
    session['nick_name'] = user.nick_name
    session['mobile'] = user.mobile
    # 6.响应登录结果
    return jsonify(errno=RET.OK, errmsg='登录成功')


@passport_blue.route('/logout')
def logout():
    """退出登录
    1.只需要将状态保持的信息删除即可（退出登录的方案有很多，但是核心都是清除session）
    2.返回退出登录的结果
    """
    session.pop('user_id', None)
    session.pop('nick_name', None)
    session.pop('mobile', None)

    # 2.返回退出登录的结果
    return jsonify(errno=RET.OK, errmsg='退出登录成功')
